/*!  \file  Metric_impl.cpp
 *	  \brief  
 * 
 *   \details   details
 *   \author    Yordan Madzhunkov German Research School for Simulation Sciences GmbH
 *   \version   1.0
 *   \date      06/12/2013 05:41:35 PM
 *   \copyright GNU Public License.
 */

// Metric stuff

#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
using namespace std;
template <typename T>
Metric<T>::Metric (const int maxMeasurements, const char * units_ ) {
	sample = new T[maxMeasurements];
	reset();
	units = 0;
	if (units_) 
		Test::set(units, units_);
}
template <typename T>
Metric<T>::~Metric() {
	if (sample) delete [] sample; sample = 0;
	if ( units) delete []  units;  units = 0;
}
template <typename T>
void Metric<T>::addMeasurement(T val) {
	min = size == 0 || val < min ? val : min;
	max = size == 0 || val > max ? val : max;
	sample[size++] = val;
}
template <typename T>
double Metric<T>::getQuantile(const double percentage, T * tmp) {
	double ind = (size - 1 ) * percentage;
//	cout << percentage << " " << ind << endl;
	int i1 = (int) floor(ind);
	int i2 = (int) ceil(ind);
//	cout << i1 << " " << i2 << endl;
	double w1 = ind - i1; assert(w1 >= 0);
	double w2 = i2 - ind; assert(w2 >= 0);
	double res = tmp[i1];
	if (w1 + w2 > 0) {
		res = (tmp[i1] * w2 + tmp[i2] * w1)/ (w1 + w2);
	}	
	return res;
}
template <typename T>
void Metric<T>::makeFinal() { // after making final, we can't add more samples
	if (min != max && size > 0) {
		T * tmp = new T[size]; 
		memcpy(tmp, sample, sizeof(T) * size); // copy the meiasurements
		std::sort(tmp, tmp + size);						// sort them
		// calculate min, max, 1st, 2nd, 3rd quater
		assert(min == tmp[0]);	assert(max == tmp[size-1]);
		var = 0.0; avg = 0.0;
		for (int i = 0; i < size; i++) {
			var += tmp[i] * tmp[i];
			avg += tmp[i];
		}
		const double rn = 1.0/size;
		var *= rn; avg *= rn; var -= avg * avg;
		sigma = sqrt(var);
		first  = getQuantile(0.25, tmp);
		median = getQuantile(0.50, tmp);
		third  = getQuantile(0.75, tmp);
		if (tmp) delete [] tmp; tmp = 0;
	}	else {
		var = 0.0; sigma = 0.0; 
		avg = min;	median = min; third = min; first = min;
	}

		
}
template <typename T>
void Metric<T>::reset() { 		// start with new set of measurements
	size = 0; min = 0; max = 0;
}
template <typename T>
void Metric<T>::storeMetrics(double * RS results) const { 
	if (results) {
		results[0] = getAverage      ();
		results[1] = getSigma        ();
		results[2] = getMin          ();
		results[3] = getFirstQuater  ();
		results[4] = getMedian       ();
		results[5] = getThirdQuater  ();
		results[6] = getMax          ();
	}
}
template <typename T>
std::ostream& operator<<(std::ostream& os, const Metric<T> & m) {
	if (m.getMin() != m.getMax()) {	
		os << ""   << m.getAverage        () ;
		os << " +/- "<< m.getSigma          () ;
		os << "  ("<< m.getMin            () ;
		os << ", " << m.getFirstQuater    () ;
		os << ", " << m.getMedian         () ;
		os << ", " << m.getThirdQuater    () ;
		os << ", " << m.getMax            () ;
		os << ") " << endl; ;
	} else {
		os <<  m.getMax ()  << endl;
	}	
	return os;
}
